package com.db.router.strategy.impl;

import com.db.router.context.DBContextHolder;
import com.db.router.strategy.ConsistentHashing;
import com.db.router.strategy.DBRouterStrategyBase;
import com.db.router.strategy.IDBRouterStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component("DBRouterStrategyConsistentHash")
public class DBRouterStrategyConsistentHash extends DBRouterStrategyBase implements IDBRouterStrategy {

    private final Logger logger = LoggerFactory.getLogger(DBRouterStrategyConsistentHash.class);

    private ConsistentHashing consistentHashing;

    @Override
    public void init() {
        int dbCount = dbRouterConfig.getDbCount();
        int tbCount = dbRouterConfig.getTbCount();
        int size = dbCount * tbCount;
        ConsistentHashing.Node[] nodes = new ConsistentHashing.Node[size];
        for (int i = 0; i < dbCount; i++) {
            for (int j = 0; j < tbCount; j++) {
                nodes[i*tbCount+j] = new ConsistentHashing.Node(i,j);
            }
        }
        consistentHashing =  new ConsistentHashing(nodes,80);
    }

    @Override
    public void doRouter(String dbKeyAttr) {
        int index = fnv132hash(dbKeyAttr);

        ConsistentHashing.Node node = consistentHashing.getNode(index);
        int dbIndex = node.getDbIndex();
        int tbIndex = node.getTbIndex();

        DBContextHolder.setDBKey(String.format("%02d", dbIndex));
        DBContextHolder.setTBKey(String.format("%03d", tbIndex));
        logger.debug("数据库路由 dbIdx：{} tbIdx：{}",  dbIndex, tbIndex);

    }

    private int fnv132hash(String key) {
        final int p = 16777619;
        int hash = (int) 2166136261L;
        for (int i = 0; i < key.length(); i++) {
            hash = (hash ^ key.charAt(i)) * p;
        }
        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;

        // 如果算出来的值为负数则取其绝对值
        return Math.abs(hash);
    }
}
